package com.naiterui.ehp.bs.patient.service.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.TreeSet;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.naiterui.common.repository.page.CustomizedPageConvertor;
import com.naiterui.common.repository.page.Page;
import com.naiterui.common.repository.page.PageVO;
import com.naiterui.ehp.bp.domain.Department;
import com.naiterui.ehp.bp.domain.Hospital;
import com.naiterui.ehp.bp.domain.Patient;
import com.naiterui.ehp.bp.support.exception.BusinessException;
import com.naiterui.ehp.bp.utils.date.DateUtil;
import com.naiterui.ehp.bp.utils.lang.StringUtil;
import com.naiterui.ehp.bs.patient.dao.IPatientDao;
import com.naiterui.ehp.bs.patient.dao.mongo.ApPatientCaseFileDao;
import com.naiterui.ehp.bs.patient.exception.ExceptionCodes;
import com.naiterui.ehp.bs.patient.prodocl.MCaseFile;
import com.naiterui.ehp.bs.patient.prodocl.MCaseFileImg;
import com.naiterui.ehp.bs.patient.repository.IApDepartmentRepository;
import com.naiterui.ehp.bs.patient.service.IHospitalService;
import com.naiterui.ehp.bs.patient.service.IPatientCaseFileService;
import com.naiterui.ehp.bs.patient.utils.ImgUtil;
import com.naiterui.ehp.bs.patient.utils.cache.BaseParamCacheUtil;
import com.naiterui.ehp.bs.patient.utils.constants.PatientConstant;
import com.naiterui.ehp.bs.patient.vo.casefile.CaseDetailVO;
import com.naiterui.ehp.bs.patient.vo.casefile.CaseFileBaseVO;
import com.naiterui.ehp.bs.patient.vo.casefile.CaseImgVO;
import com.naiterui.ehp.bs.patient.vo.casefile.CaseListVO;
import com.naiterui.ehp.bs.patient.vo.parameters.DepartmentVO;

/**
 * 患者病历档案业务实现
 * <P>File name : PatientCaseFileServiceImpl.java </P>
 * <P>Author : NTR.Amber </P>
 * <P>Date : 2015年9月13日 </P>
 */
@Service
public class ApPatientCaseFileServiceImpl implements IPatientCaseFileService {

    private static final Logger LOGGER = LoggerFactory.getLogger(ApPatientCaseFileServiceImpl.class);

    @Autowired
    private ApPatientCaseFileDao apPatientCaseFileDao;

    @Autowired
    private IPatientDao patientDao;

    @Autowired
    private IApDepartmentRepository departmentRepository;

    @Autowired
    private IHospitalService hospitalService;

    @Override
    public PageVO<CaseListVO> getCaseFileList(Long patientId, int page, int num) {
        // 分页查询患者病例
        Page<MCaseFile> casePage = this.apPatientCaseFileDao.getCaseByPage(patientId, page, num);
        // 构造返回VO
        PageVO<CaseListVO> resultPage = new PageVO<>();

        List<CaseListVO> results = new ArrayList<>();

        for (MCaseFile caseFile : casePage.getResult()) {
            CaseListVO caseListVO = new CaseListVO();
            this.buildCaseBaseVO(caseFile, caseListVO);

            // 构建排序后病例图片list
            // 医嘱图片 处方单图片 检验单
            @SuppressWarnings("unchecked")
            List<CaseImgVO> imgs = this.sortCaseImgByTime(caseFile.getAdviceList(), caseFile.getPrescriptionList(), caseFile.getCheckList());
            if (imgs.size() > PatientConstant.CASE_LIST_IMG_SIZE) {
                // 截取最新四张
                imgs = imgs.subList(0, PatientConstant.CASE_LIST_IMG_SIZE);
            }
            // 设置图片列表
            caseListVO.setImgs(imgs);
            // 病历档案VO加入数组
            results.add(caseListVO);
        }

        // 设置分页结果集
        resultPage = CustomizedPageConvertor.pageConvert(casePage, results);
        return resultPage;
    }

    @SuppressWarnings("unchecked")
    @Override
    public CaseDetailVO getCaseDetail(Long patientId, String caseId) throws BusinessException {
        // 查找病历档案
        MCaseFile caseFile = this.apPatientCaseFileDao.getCaseByPatient(patientId, caseId);
        // 构造返回基本信息
        CaseDetailVO detailVO = new CaseDetailVO();
        if (null == caseFile) {
            LOGGER.error("---------- getCaseDetail Error ; case not exist : caseId {}", caseId);
            throw new BusinessException(ExceptionCodes.PATIENT_CASE_FILE_NOT_EXIST);
        }
        this.buildCaseBaseVO(caseFile, detailVO);
        // 图片信息
        // 医嘱图片
        detailVO.setAdviceList(this.sortCaseImgByTime(caseFile.getAdviceList()));
        // 处方单图片
        detailVO.setPrescriptionList(this.sortCaseImgByTime(caseFile.getPrescriptionList()));
        // 检验单
        detailVO.setCheckList(this.sortCaseImgByTime(caseFile.getCheckList()));

        return detailVO;
    }

    /**
     * 排序图片信息List
     * PatientCaseFileServiceImpl.sortCaseImgByTime()<BR>
     * <P>Author : Gaoll </P>
     * <P>Date : 2015年9月23日 </P>
     *
     * @param imgLists 待排序档案图片
     *
     * @return
     */
    private List<CaseImgVO> sortCaseImgByTime(@SuppressWarnings("unchecked") List<MCaseFileImg>... imgLists) {
        // 按照图片上传时间排序，最近上传图片排位靠前
        TreeSet<CaseImgVO> imgTreeSet = new TreeSet<>(new Comparator<CaseImgVO>() {
            @Override
            public int compare(CaseImgVO img1, CaseImgVO img2) {
                // 适配历史数据，图片无上传时间,按照DB默认排序 Change By Gaoll - 2015-9-24 09:29:40
                if (null == img1.getUploadTime() || null == img2.getUploadTime()) {
                    return 1;
                }
                int compareResult = img2.getUploadTime().compareTo(img1.getUploadTime());
                // 时间相同，按照DB默认排序 Change By Gaoll - 2015-9-24 09:29:40
                return (compareResult < 0) ? -1 : 1;
            }
        });
        for (List<MCaseFileImg> imgList : imgLists) {
            for (MCaseFileImg caseFileImg : imgList) {
                CaseImgVO imgVO = this.buildCaseImgVO(caseFileImg);
                imgTreeSet.add(imgVO);
            }
        }
        return new ArrayList<>(imgTreeSet);
    }

    @SuppressWarnings("unchecked")
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CaseDetailVO savePatientCaseFile(Patient patient, CaseDetailVO detailVO) throws BusinessException {
        // 保存目标
        MCaseFile caseFile;
        if (StringUtils.isNotBlank(detailVO.getId())) {
            // 编辑
            // 获取档案实体
            caseFile = this.apPatientCaseFileDao.getCaseByPatient(patient.getId(), detailVO.getId());
            if (caseFile == null) {
                // 患者与病例不符
                LOGGER.error("---------- savePatientCaseFile ERROR ; case and patient mismatching : patientId {}, caseId {}", patient,
                             detailVO.getId());
                throw new BusinessException(ExceptionCodes.ILLEGAL_REQUEST);
            }
            caseFile.setChangedBy(patient.toString());
        } else {
            // 新增，创建目标对象
            caseFile = new MCaseFile();
            // 关联患者
            caseFile.setPatientId(patient.getId());
            caseFile.setCreatedBy(patient.toString());

            // 新增病历档案，判断用户是否第一次上传
            if (patient.getFirstCaseTag() == 0) {// 未上传过病历档案（不可靠，2.0之前版本没有此标签，之前可能部分用户已上传过病历档案）
                Page<MCaseFile> mf = this.apPatientCaseFileDao.getCaseByPage(patient.getId(), 1, 1);
                if (mf.getTotalCount() > 0) { // 有病历档案记录，说明在添加标签之前上传过病历档案
                    // 非首次上传
                    detailVO.setFirst(false);
                } else {
                    // 首次上传病历档案
                    detailVO.setFirst(true);
                }
                // 此次上传后，修改患者标签为已上传
                patient.setFirstCaseTag(1);
                this.patientDao.saveOrUpdate(patient);
            } else {// 已上传过病例档案，非首次上传
                detailVO.setFirst(false);
            }
        }
        caseFile.setDescription(detailVO.getDescription());
        caseFile.setVistingTime(detailVO.getVistingTime());
        caseFile.setDoctor(detailVO.getDoctor());
        caseFile.setDepartment(detailVO.getDepartment());
        caseFile.setDepartmentId(detailVO.getDepartmentId());
        caseFile.setHospitalId(detailVO.getHospitalId());
        caseFile.setHospital(detailVO.getHospital());
        // 图片处理 新增档案才有图片信息。编辑档案图片信息单独处理
        // 持久化图片信息--医嘱
        List<MCaseFileImg> adviceList = new ArrayList<>();
        for (CaseImgVO imgVO : detailVO.getAdviceList()) {
            MCaseFileImg caseImg = new MCaseFileImg();
            caseImg.setUploadTime(imgVO.getUploadTime());
            caseImg.setImgUrl(imgVO.getImgUrl().replace(ImgUtil.getCaseImgDomain(), ""));
            adviceList.add(caseImg);
        }
        caseFile.setAdviceList(adviceList);

        // 持久化图片信息--处方单
        List<MCaseFileImg> prescriptionList = new ArrayList<>();
        for (CaseImgVO imgVO : detailVO.getPrescriptionList()) {
            MCaseFileImg caseImg = new MCaseFileImg();
            caseImg.setUploadTime(imgVO.getUploadTime());
            caseImg.setImgUrl(imgVO.getImgUrl().replace(ImgUtil.getCaseImgDomain(), ""));
            prescriptionList.add(caseImg);
        }
        caseFile.setPrescriptionList(prescriptionList);

        // 持久化图片信息--检验单
        List<MCaseFileImg> checkList = new ArrayList<>();
        for (CaseImgVO imgVO : detailVO.getCheckList()) {
            MCaseFileImg caseImg = new MCaseFileImg();
            caseImg.setUploadTime(imgVO.getUploadTime());
            // 去掉域名，存储相对地址
            caseImg.setImgUrl(imgVO.getImgUrl().replace(ImgUtil.getCaseImgDomain(), ""));
            checkList.add(caseImg);
        }
        caseFile.setCheckList(checkList);
        // 修改病例
        if (!StringUtils.isEmpty(caseFile.getUuid())) {
            caseFile.setChangedAt(new Date());
        } else {
            String uuid = StringUtil.formatUUID();
            caseFile.setUuid(uuid);
        }
        caseFile.setFetchStatus(MCaseFile.FINISH);
        this.apPatientCaseFileDao.saveOrUpdate(caseFile);
        // 获取科室医院信息
        CaseDetailVO detail = new CaseDetailVO();
        this.buildCaseBaseVO(caseFile, detail);
        caseFile.setDepartment(detail.getDepartment());
        caseFile.setHospital(detail.getHospital());

        // 处理图片url（拼接域名 + 上传时间排序）
        detailVO.setAdviceList(this.sortCaseImgByTime(caseFile.getAdviceList()));
        // 处方单图片
        detailVO.setPrescriptionList(this.sortCaseImgByTime(caseFile.getPrescriptionList()));
        // 检验单
        detailVO.setCheckList(this.sortCaseImgByTime(caseFile.getCheckList()));

        // 病历档案id
        detailVO.setId(caseFile.getId());

        return detailVO;
    }

    /**
     * 删除病例档案
     * <P>Author : Amber </P>
     * <P>Date : 2015年9月14日 </P>
     *
     * @param patient
     * @param caseId
     *
     * @see com.naiterui.ehp.bs.patient.service.IPatientCaseFileService#deleteCaseFile(Patient,
     * java.lang.String)
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteCaseFile(Patient patient, String caseId) throws BusinessException {
        MCaseFile caseFile = this.apPatientCaseFileDao.getCaseByPatient(patient.getId(), caseId);
        if (caseFile == null) {
            // 删除目标不存在
            LOGGER.error("---------- deleteCaseFile ERROR ; casefile not exist : patientId {},caseId {}", patient, caseId);
            throw new BusinessException(ExceptionCodes.ILLEGAL_REQUEST);
        }

        // 删除时，老用户首次上传状态同步处理
        if (patient.getFirstCaseTag() == 0) {
            patient.setFirstCaseTag(1);
            this.patientDao.saveOrUpdate(patient);
        }
        this.apPatientCaseFileDao.deleteCaseFile(caseFile);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCaseImgByCms(String operator, String caseId, Integer type, List<CaseImgVO> newList) throws BusinessException {
        // 数据校验
        MCaseFile caseFile = this.apPatientCaseFileDao.getById(caseId);
        if (caseFile == null) {
            // 病例档案不存在，记录日志，抛异常
            LOGGER.error("---------- updateCaseImgByCms ERROR ; case not exist : caseId {}", caseId);
            throw new BusinessException(ExceptionCodes.ILLEGAL_REQUEST);
        }
        // 数据更新
        this.doUpdateCaseImgs(operator, type, newList, caseFile);
        // cms 接口图片回显url拼接图片服务器前轴
        for (CaseImgVO caseImgVO : newList) {
            caseImgVO.setImgUrl(ImgUtil.getCaseImgUrl(caseImgVO.getImgUrl()));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateCaseImgs(Long patientid, String caseId, Integer type, List<CaseImgVO> newList) throws BusinessException {
        // 数据校验
        MCaseFile caseFile = this.apPatientCaseFileDao.getCaseByPatient(patientid, caseId);
        Patient patient = this.patientDao.get(patientid);
        if (caseFile == null) {
            // 病例档案不存在，记录日志，抛异常
            LOGGER.error("---------- updateCaseImgs ERROR ; case not exist : caseId {}", caseId);
            throw new BusinessException(ExceptionCodes.ILLEGAL_REQUEST);
        }
        // 数据更新
        this.doUpdateCaseImgs(patient.getName(), type, newList, caseFile);
    }

    private void doUpdateCaseImgs(String operator, Integer type, List<CaseImgVO> newList, MCaseFile caseFile) {
        // 更新列表
        List<MCaseFileImg> result = new ArrayList<>();
        // 前端新列表为null全部删除
        if (newList != null) {
            for (CaseImgVO imgVO : newList) {
                MCaseFileImg img = new MCaseFileImg();
                img.setUploadTime(imgVO.getUploadTime());
                img.setImgUrl(imgVO.getImgUrl().replace(ImgUtil.getCaseImgDomain(), ""));
                result.add(img);
            }
        }

        // 按照类型更新图片
        if (PatientConstant.CASE_IMG_TYPE_ADVICE == type) {// 医嘱
            caseFile.setAdviceList(result);
        } else if (PatientConstant.CASE_IMG_TYPE_PRESCRIPTION == type) {// 处方单
            caseFile.setPrescriptionList(result);
        } else if (PatientConstant.CASE_IMG_TYPE_CHECK == type) {// 检验单
            caseFile.setCheckList(result);
        }

        // 记录操作人，操作时间
        caseFile.setChangedAt(new Date());
        caseFile.setChangedBy(operator);
        caseFile.setFetchStatus(MCaseFile.FINISH);
        // 更新病例
        this.apPatientCaseFileDao.updateCaseFile(caseFile);
        // 获取科室医院信息
        CaseDetailVO detailVO = new CaseDetailVO();
        this.buildCaseBaseVO(caseFile, detailVO);
        caseFile.setDepartment(detailVO.getDepartment());
        caseFile.setHospital(detailVO.getHospital());
    }

    /**
     * 构造病例基础信息VO
     * PatientCaseFileServiceImpl.buildCaseListVO()<BR>
     * <P>Author : Amber </P>
     * <P>Date : 2015年9月13日 </P>
     *
     * @param caseFile
     *
     * @return
     */
    private void buildCaseBaseVO(MCaseFile caseFile, CaseFileBaseVO caseFileBaseVO) {
        caseFileBaseVO.setId(caseFile.getId());
        // 科室处理
        if (caseFile.getDepartmentId() != null && caseFile.getDepartmentId() != 0) {// 存在科室，且非自定义-
            // 按照科室id查找科室信息
            // 缓存获取科室信息
            DepartmentVO departmentVO = BaseParamCacheUtil.getDepartmentFromRedis(caseFile.getDepartmentId());
            if (departmentVO != null) {
                // 缓存有指定科室
                caseFileBaseVO.setDepartmentId(departmentVO.getId());
                caseFileBaseVO.setDepartment(departmentVO.getName());
            } else {
                // 缓存没有科室信息，数据库获取
                Department department = this.departmentRepository.get(caseFile.getDepartmentId());
                caseFileBaseVO.setDepartmentId(department.getId());
                caseFileBaseVO.setDepartment(department.getName());
            }
        } else {
            caseFileBaseVO.setDepartmentId(caseFile.getDepartmentId());
            caseFileBaseVO.setDepartment(caseFile.getDepartment() == null ? "" : caseFile.getDepartment());
        }

        // 处理医院信息
        if (caseFile.getHospitalId() != null) {
            // 选择医院
            Hospital hospital = this.hospitalService.getHospitalById(caseFile.getHospitalId());
            if (hospital != null) {// 以数据库内医院名字初始化医院信息
                caseFileBaseVO.setHospitalId(hospital.getId());
                caseFileBaseVO.setHospital(hospital.getName());
            } else {// 数据库没有，一参数初始化医院信息
                caseFileBaseVO.setHospitalId(caseFile.getHospitalId());
                caseFileBaseVO.setHospital(caseFile.getHospital());
            }
        } else {
            // 手动填写医院
            caseFileBaseVO.setHospital(caseFile.getHospital());
        }
        caseFileBaseVO.setDoctor(caseFile.getDoctor());
        caseFileBaseVO.setDescription(caseFile.getDescription());
        caseFileBaseVO.setVistingTime(caseFile.getVistingTime());
        // 病历档案上传时间
        if (null != caseFile.getCreatedAt()) {
            caseFileBaseVO.setCreatedAt(DateUtil.formatDate(caseFile.getCreatedAt(), DateUtil.DATE_TIME_FORMAT));
        }
    }

    /**
     * 构造病历档案图片信息VO
     * PatientCaseFileServiceImpl.buildCaseImgVO()<BR>
     * <P>Author : Amber </P>
     * <P>Date : 2015年9月13日 </P>
     *
     * @param caseImg
     *
     * @return
     */
    private CaseImgVO buildCaseImgVO(MCaseFileImg caseImg) {
        CaseImgVO caseImgVO = new CaseImgVO();
        caseImgVO.setUploadTime(caseImg.getUploadTime());
        // 图片地址，追加域名
        caseImgVO.setImgUrl(ImgUtil.getCaseImgUrl(caseImg.getImgUrl()));
        return caseImgVO;
    }

    @Override
    public List<CaseImgVO> getCaseFileImgsByType(String caseId, Integer type) throws BusinessException {

        // 获取病历档案
        MCaseFile caseFile = this.apPatientCaseFileDao.getById(caseId);
        if (caseFile == null) {
            // 档案不存在
            LOGGER.error("---------- getCaseFileImgsByType Error ; case not exist : caseId {}", caseId);
            throw new BusinessException(ExceptionCodes.ILLEGAL_REQUEST);
        }
        // 指定类型图片
        List<MCaseFileImg> mCaseFileImgs = new ArrayList<>();
        if (PatientConstant.CASE_IMG_TYPE_ADVICE == type) {
            mCaseFileImgs = caseFile.getAdviceList();
        } else if (PatientConstant.CASE_IMG_TYPE_PRESCRIPTION == type) {
            mCaseFileImgs = caseFile.getPrescriptionList();
        } else if (PatientConstant.CASE_IMG_TYPE_CHECK == type) {
            mCaseFileImgs = caseFile.getCheckList();
        }

        // 处理返回
        List<CaseImgVO> imgs = new ArrayList<>();
        for (MCaseFileImg img : mCaseFileImgs) {
            CaseImgVO imgVO = this.buildCaseImgVO(img);
            imgs.add(imgVO);
        }
        return imgs;
    }

    @Override
    public Boolean haveCaseFile(Long patientId) throws BusinessException {
        MCaseFile caseFile = this.apPatientCaseFileDao.getFirstCase(patientId);
        return caseFile != null;
    }

}
